#! /usr/bin/env python
# -*- encoding: utf-8 -*-
"""
@File:      tc_libffi_libffi_func_002.py
@Time:      2024年02月22日16:06:08
@Author:    Liujiang <wb-lj931787@alibaba-inc.com>
@License:   Mulan PSL v2
@Modified:  Liujiang <wb-lj931787@alibaba-inc.com>
"""

from common.basetest import LocalTest
import ctypes

class Test(LocalTest):
    """
    Confirm that libffi can throw appropriate exceptions in error and 
    exception situations through the ctypes library.

    The output of the test program is similar to "ffi_prep_cif returned an error: X", \
    where X is a non-zero error code (such as FFI-BAD-TYPEDEF). \
    The program should not crash or exhibit uncertain behavior.
    See tc_libffi_libffi_func_002.yaml for details

    :avocado: tags=P1,noarch,local,fixed
    """

    PARAM_DIC = {"pkg_name": "libffi libffi-devel"}
    def setUp(self):
        super().setUp(self.PARAM_DIC)
        self.cmd("rpm -qa |grep 'libffi'")
        cmdline_generate_function = '''cat > invalid_function.c <<EOF
void invalid_function(int a, float b) {
}      
EOF
'''
        cmdline_generate_test = '''cat > libffi_test.c <<EOF
#include <stdio.h>
#include <dlfcn.h>
#include <ffi.h>

int main() {
    void* handle = dlopen("invalid_function.so", RTLD_LAZY);
    void (*invalid_func)(int, float) = (void (*)(int, float))dlsym(handle, "invalid_function");

    ffi_cif cif;
    ffi_type bad_type = {.size = 0, .alignment = 0, .type = FFI_TYPE_VOID, .elements = NULL};
    // ffi_type* args[2] = {&ffi_type_sint, &ffi_type_double};
    // ffi_type* args[2] = {&ffi_type_double, &ffi_type_double};
    ffi_type* args[2] = {&ffi_type_sint, &bad_type};
    int arg1 = 1;
    float arg2 = 2.0f;
    void* values[2] = {&arg1, &arg2};

    ffi_status status = ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_void, args);
    if (status != FFI_OK) {
        printf("ffi_prep_cif returned an error: %d", status);
    } else {
        printf("Testing should not print this line.");
    }

    dlclose(handle);
    return 0;
}
EOF
'''
        self.cmd(cmdline_generate_function)
        self.cmd(cmdline_generate_test)
        
    def test(self):
        self.cmd("gcc -shared -fPIC -o invalid_function.so invalid_function.c")
        code, libffi_result = self.cmd("file invalid_function.so")
        self.assertIn("dynamically linked", libffi_result)

        self.cmd("gcc -o libffi_test libffi_test.c -ldl -lffi")
        code, libffi_result = self.cmd("file libffi_test")
        self.assertIn("dynamically linked", libffi_result)

        code, libffi_test_ret = self.cmd("export LD_LIBRARY_PATH=./; ./libffi_test")
        self.assertIn("ffi_prep_cif", libffi_test_ret, msg=
                      '''The output of the test program is similar to "ffi_prep_cif returned an error: X", \
                      where X is a non-zero error code (such as FFI-BAD-TYPEDEF).
                      The program should not crash or exhibit uncertain behavior.''')

    def tearDown(self):
        super().tearDown(self.PARAM_DIC)
        self.cmd("rm -rf invalid_function.c libffi_test.c invalid_function.so libffi_test")
